/*
Created on Sept. 28th 2005
(C) 2005 
@author Michael J Ocean

This program is free software; you can redistribute it and/or
modify it under the terms of Version 2 of the GNU General Public 
License.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

package sxe.sensors;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.StringTokenizer;

import javax.media.Buffer;
import javax.media.CannotRealizeException;
import javax.media.CaptureDeviceInfo;
import javax.media.Format;
import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.NoDataSourceException;
import javax.media.NoPlayerException;
import javax.media.Player;
import javax.media.control.FormatControl;
import javax.media.control.FrameGrabbingControl;
import javax.media.format.IndexedColorFormat;
import javax.media.format.VideoFormat;
import javax.media.protocol.CaptureDevice;
import javax.media.protocol.DataSource;
import javax.media.util.BufferToImage;

import snbenchlib.XML;
import snobject.snImage;
import snobject.snNil;
import snobject.snObject;

public class ImageSensor extends GenericSensor {

	private CaptureDeviceInfo m_captureDeviceInfo = null;
	private FrameGrabbingControl m_frameGrabbingControl = null;
	private Format m_lastFormatUsed = null;
	private DataSource m_videoDataSource = null;
	private Player m_player = null;
	
	private void doConstructor(CaptureDeviceInfo devInfo){
		m_captureDeviceInfo = devInfo;
		m_state = GenericSensor.STATE_PREINIT;
		m_type = GenericSensor.TYPE_IMAGE;
		m_uri = m_type + "/" + m_id;
	}
	
	public ImageSensor() {
		super();
		doConstructor(null);
	}
	
	public ImageSensor(CaptureDeviceInfo devInfo){
		super();
		doConstructor(devInfo);
	}

	public String GetInfoXML(){
		String b = new String();
		
		if(m_captureDeviceInfo!=null){
			b += "  <name>" + (m_captureDeviceInfo.getName()) + "</name>\n";

			b+= "  <deviceFormats>\n";
			Format deviceFormat[] = m_captureDeviceInfo.getFormats();
			for (int y = 0; y < deviceFormat.length; y++)
			{
				if (deviceFormat[y] instanceof VideoFormat){
					VideoFormat vf = (VideoFormat)deviceFormat[y];
					b+="    <videoFormat>\n";
					Dimension d = vf.getSize();
					b +="      <resolution>\n";
					b += XML.tagger(7,"width",String.valueOf((int) d.getWidth()));
					b += XML.tagger(7,"height",String.valueOf((int) d.getHeight()));
					b +="       </resolution>\n";
					if (vf instanceof IndexedColorFormat)
					{
						IndexedColorFormat icf = (IndexedColorFormat) vf;
						b += XML.tagger(6,"colordepth",String.valueOf(icf.getMapSize()));
					}

					b+= XML.tagger(6,"encoding",vf.getEncoding());
					b+= XML.tagger(6,"maxlength",String.valueOf(vf.getMaxDataLength()));
					b+="    </videoFormat>\n";
				}
			}
			b+= "  </deviceFormats>\n";
		}
		return super.GetInfoXML(b);
	}
	
	public boolean initializeSensor() {
		if(m_captureDeviceInfo==null){
			m_state = GenericSensor.STATE_FAILED;
			return false;
		}
			
		MediaLocator videoMediaLocator = m_captureDeviceInfo.getLocator();
		if(videoMediaLocator==null){
			/* if this occurs, the device is otherwise unusable, representing
			 * either a stack inconsistency or a serious device error.
			 */
			m_state = GenericSensor.STATE_FAILED;
			return false;
		}
		
		try {
			m_videoDataSource = javax.media.Manager.createDataSource(videoMediaLocator);
		} catch (NoDataSourceException e) {
			// TODO Auto-generated catch block
			//e.printStackTrace();
			m_videoDataSource = null;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			//e.printStackTrace();
			m_videoDataSource = null;
		}
		if(m_videoDataSource==null){
			m_state = GenericSensor.STATE_FAILED;
			return false;
		}		

		Format newformat = getCorrespondingFormat(m_captureDeviceInfo,"320x240");
		setCapFormat(newformat);
		
		try {
			m_player = Manager.createRealizedPlayer(m_videoDataSource);
			m_player.start();
		} catch (NoPlayerException e1) {
			e1.printStackTrace();
			m_player = null;
		} catch (CannotRealizeException e1) {
			e1.printStackTrace();
			m_player = null;
		} catch (IOException e1) {
			e1.printStackTrace();
			m_player = null;
		}

		if(m_player==null){
			/** ERROR **/
			System.out.println("m_player is null after create realized.");
			m_state = GenericSensor.STATE_FAILED;
			return false;
		}

	    try {
			/* Wait a few seconds for camera to init
			 * (otherwise grabbed images will be null) 
			 * TODO: there *must* be a better solution to this 
			 */
			Thread.sleep(2500);
		} catch (InterruptedException e) {}
	

	    m_frameGrabbingControl =  (FrameGrabbingControl)m_player.getControl("javax.media.control.FrameGrabbingControl");
	    if(m_frameGrabbingControl==null){
			/** ERROR **/
	    	System.out.println("framegrabbingcontrol is null on init.");
			m_state = GenericSensor.STATE_FAILED;
			return false;
		}
	
    m_state = STATE_INIT;
	return true;	
	}


	public boolean isAlive() {
		// TODO Auto-generated method stub
		
		return true;
	}

	public snObject getData(Object oParam) {
		/* change format if requested/required */
		Format newformat = null;
		
		if(oParam!=null){
			String sFormat = (String)oParam;
			newformat = getCorrespondingFormat(m_captureDeviceInfo,sFormat);
		}
		if(setCapFormat(newformat)){
			Buffer buf = m_frameGrabbingControl.grabFrame();
			
			if(buf!=null){
		        
				/* BufferedImages are full-featured, so we'll copy our image into a buffered
				 * image.  We use the Graphics2D object to draw the content of our grabbed
				 * frame into the bufferedImage.
				 */
		        Image img = (new BufferToImage((VideoFormat)buf.getFormat()).createImage(buf));
		        BufferedImage buffImg = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB);
		        Graphics2D g = buffImg.createGraphics();
			    g.drawImage(img,null,null);

		        /* finally, return the captured image (as a bufferedImage) */ 
		        return new snImage(buffImg);   
	        }
		}
		m_state = STATE_FAILED;
		return snNil.NIL;
	}

	public boolean shutDown() {
		if(m_player != null){
			m_player.close();
			m_player.deallocate();
			m_player = null;
		}
		
		m_state = GenericSensor.STATE_PREINIT;
		return true;
	}

	/******************/
	private static Format getCorrespondingFormat(CaptureDeviceInfo cdi, String sFormatDesc){

		StringTokenizer st = new StringTokenizer(sFormatDesc,"x");
		int width = Integer.parseInt(st.nextToken());
		int height = Integer.parseInt(st.nextToken());
		
		Format deviceFormat[] = cdi.getFormats();
		for (int i = 0; i < deviceFormat.length; i++)
		{
			if (deviceFormat[i] instanceof VideoFormat){
				VideoFormat vf = (VideoFormat)deviceFormat[i];
				Dimension d = vf.getSize();
				if((width == d.getWidth())&&(height == d.getHeight()))
					return deviceFormat[i];
			}
		}
		return null;
	}
	static protected boolean setCaptureFormat(DataSource dataSource, Format format){

		FormatControl fcs[] = ((CaptureDevice) dataSource).getFormatControls();
		for (int i = 0; i < fcs.length; i++)
		{
			if (fcs[i] != null){
				Format supportedFormats[] = fcs[i].getSupportedFormats();
				if (supportedFormats != null){
					for (int j = 0;  j < supportedFormats.length;  j++)
						if (supportedFormats[j].matches(format)){
							fcs[i].setFormat(format);
							return true;
						}
				}
			}
		}

		return false;
	}

	private boolean setCapFormat(Format newformat){
		
		if(newformat!=null){
			if(newformat != m_lastFormatUsed){
				System.out.println("Changing format");
				if(!setCaptureFormat(m_videoDataSource,newformat)){
					/** ERROR **/
					System.out.println("Could not set format");
					m_state = GenericSensor.STATE_FAILED;
					return false;
				}
				else
					m_lastFormatUsed = newformat;
			}
		}
		return true;
	}

	/* TO DO */
	public String getResolution() {
		// TODO Auto-generated method stub
		return null;
	}
}

